package cn.roisrosy.middleware.db.router;

import cn.roisrosy.middleware.db.router.annotation.DBRouter;
import org.apache.commons.lang.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;


/***
 * 数据路由切面，通过自定义注解的方式，拦截被切面的方法，进行数据库路由
 */
@Aspect
public class DBRouterJoinPoint {
    private Logger logger = LoggerFactory.getLogger(DBRouterJoinPoint.class);
    private DBRouterConfig dbRouterConfig;
    public DBRouterJoinPoint(DBRouterConfig dbRouterConfig) {
        this.dbRouterConfig = dbRouterConfig;
    }
    @Pointcut("@annotation(cn.roisrosy.middleware.db.router.annotation.DBRouter)")
    public  void aopPoint(){

    }
    @Around("aopPoint() && @annotation(dbRouter)")
    public  Object doRouter(ProceedingJoinPoint joinPoint, DBRouter dbRouter) throws Throwable {
        String dbKey = dbRouter.key();
        if(StringUtils.isBlank(dbKey)){
            throw  new RuntimeException("annotation DBRouter key is null!");
        }
        //计算路由 拿到连接点的方法
        String dbKeyAttr = getAttrValue(dbKey, joinPoint.getArgs());
        //根据库表乘积拿到散列长度
        int size = dbRouterConfig.getDbCount()*dbRouterConfig.getTbCount();
        // 扰动函数
        int idx = (size - 1) & (dbKeyAttr.hashCode() ^ (dbKeyAttr.hashCode() >>> 16));
        // 库表索引
        int dbIdx = idx / dbRouterConfig.getTbCount() + 1;
        int tbIdx = idx - dbRouterConfig.getTbCount() * (dbIdx - 1);
        //设置到 ThreadLocal
        DBContextHolder.setDbKey(String.format("%02d",dbIdx));
        DBContextHolder.setDbKey(String.format("%03d",tbIdx));
        logger.info("数据库路由 dbIdx:{}, tbIdx:{}",dbIdx,tbIdx);
        //返回结果
        try {
            return joinPoint.proceed();
        }finally {
            DBContextHolder.clearDBKey();
            DBContextHolder.clearTBKey();

        }
    }

    public String getAttrValue(String attr,Object[] args){
        if(1==args.length){
            Object arg= args[0];
            if(arg instanceof  String){
                return  arg.toString();
            }
        }
        String filedValue = null;
        for(Object arg:args){
            try{
                if(StringUtils.isNotBlank(filedValue)){
                    break;
                }
                filedValue=String.valueOf(this.getValueByName(arg,attr));
            }catch (Exception e){
                logger.error("获取路由属性值失败 attr:{}",attr,e);
            }
        }
        return  filedValue;
    }

    /***
     * 获取对象的特定属性值
     * @param item 对象
     * @param name 属性名
     * @return 属性值
     */
    private Object getValueByName(Object item, String name){
        try{
            Field field = (Field) getFieldByName(item,name);
            if(field==null){
                return  null;
            }
            field.setAccessible(true);
            Object o =field.get(item);
            field.setAccessible(false);
            return o;
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }



    /***
     * 根据名称获取方法，该方法同时兼顾继承类获取父类的属性
     * @param item 对象
     * @param name 属性名
     * @return 该属性对应方法
     */
    private  Object getFieldByName(Object item,String name){
        try{
            Field field;
            try{
                field = item.getClass().getDeclaredField(name);
            }catch (NoSuchFieldException e){
                field =item.getClass().getSuperclass().getDeclaredField(name);
            }
            return  field;
        }catch (NoSuchFieldException e ){
            return null;
        }

    }
}
